#include "icmp.h"
#include "eth.h"
#include "ip.h"
#include "util.h"


struct rte_mbuf * _send_icmp(struct rte_mempool * pool, uint8_t * dst_mac, uint32_t sip, uint32_t dip, uint16_t id, uint16_t seqnb){
    struct rte_mbuf * m_buf = rte_pktmbuf_alloc(pool);
    if(!m_buf){
        error_print("send_icmp");
    }
    const unsigned total_length = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct  rte_icmp_hdr);
    m_buf->pkt_len = total_length;
    m_buf->data_len = total_length;

    uint8_t * pkt_data = rte_pktmbuf_mtod(m_buf, uint8_t *);
    encode_icmp_pkt(pkt_data, dst_mac, sip, dip, id, seqnb);
    return m_buf;
}

void icmp_echo_request(struct rte_mbuf * mbuf, struct inout_ring * ring, struct rte_mempool * pool){
    struct rte_ether_hdr * ehdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
    struct rte_ipv4_hdr * iphdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
    struct rte_icmp_hdr * icmphdr = (struct rte_icmp_hdr *)(iphdr + 1);
    if(icmphdr->icmp_type == RTE_IP_ICMP_ECHO_REQUEST){
        struct rte_mbuf * tx_buf = _send_icmp(pool, ehdr->src_addr.addr_bytes, iphdr->dst_addr, iphdr->src_addr, icmphdr->icmp_ident, icmphdr->icmp_seq_nb);
        rte_ring_mp_enqueue_burst(ring->out, (void **)&tx_buf, 1, NULL);
        rte_pktmbuf_free(mbuf);
    }
}

void encode_icmp_replay_hdr_pkt(uint8_t * msg, uint16_t id, uint16_t seqnb){
    struct rte_icmp_hdr * icmphdr = (struct rte_icmp_hdr *)(msg + sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));
    icmphdr->icmp_type = RTE_IP_ICMP_ECHO_REPLY;
    icmphdr->icmp_code = 0;
    icmphdr->icmp_ident = id;
    icmphdr->icmp_seq_nb = seqnb;
    icmphdr->icmp_cksum = 0;
    icmphdr->icmp_cksum = icmp_checksum((uint16_t*)icmphdr, sizeof(struct rte_icmp_hdr));
}

int encode_icmp_pkt(uint8_t * msg, uint8_t * dst_mac, uint32_t sip, uint32_t dip, uint16_t id, uint16_t seqnb){
    encode_ether_hdr_pkt(msg, dst_mac, g_src_mac);
    uint16_t total_len = sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr);
    encode_ip_hdr_pkt(msg,total_len,IPPROTO_ICMP, sip, dip);
    encode_icmp_replay_hdr_pkt(msg,id,seqnb);
    return 0;
}


